/*
 * @Description:代码优化，开始把游戏分成了“第一次游戏”和“重新开始游戏”，有大量代码重复。优化：把第一次游戏合并到重新开始游戏中
 * @Author: bcygsws@163.com
 * @Date: 2020-11-06 23:50:39
 * @LastEditTime: 2020-11-09 15:14:02
 * @FilePath: \12-TODOSe:\Web-project\process25-typescript\code\ts7_game\index.ts
 */
/* 
优化思路
1.把“重新开始游戏”点击按钮的事件处理函数，变为声明式StartGame
2.把steps userPlayer的初始值省略掉，值省略了，要添加相应的类型注解了
3.移除掉之前初始化操作时，给单元格绑定的事件-代码111-125行


*/
/**
 * 单击单元格
 * 1.获取到所有单元格列表
 * 2.遍历单元格列表
 * 3.为每个单元格添加点击事件
 */
// 是一个伪数组，伪数组也有按索引访问和length属性
let cells = document.querySelectorAll('.cell');
// 获取bord对象
let bord = document.querySelector('#bord');
//二维数组-判赢数组
let winArr = [
	[0, 1, 2],
	[3, 4, 5],
	[6, 7, 8],
	[0, 3, 6],
	[1, 4, 7],
	[2, 5, 8],
	[0, 4, 8],
	[2, 4, 6]
];
// 优化1-变量存储角色，实现代码2
// let action = 'x';

// 优化2-枚举变量存储角色
enum Player {
	X = 'x',
	O = 'o'
}
//  判赢信息显示面板
let userPlayer: Player;
// 声明变量steps,记录已经下棋的次数
let steps: number;
let message = document.querySelector('#message') as HTMLDivElement;
let btn = document.querySelector('#restart') as HTMLButtonElement;
let p = document.querySelector('#winner') as HTMLParagraphElement;
// 将点击添加x的事件处理函数抽离
/**
 * handleClick函数实现功能
 * 1.被点击的cell添加内容
 * 2.下棋的次数+1
 * 3.当前角色判赢
 * 4.移除bord当前角色的类样式，切换角色，为bord添加另外一种角色的类样式(为下一次角色交替展示进入提醒信息)
 *
 */
function handleClick(event) {
	console.log(event);
	//  event.target本质上是一个cell,且有类型注解
	let target = event.target as HTMLDivElement;
	//  html模板中初始化时，#bord中放置的是x类，规定了先放x后放o
	// 实现方式1；
	// if (bord.classList.contains('x')) {
	// 	target.classList.add('x');
	// 	// x内容真实地填充到方格后，移除#bord的x类，置换成o类。下次放置提醒的是灰色的o
	// 	bord.classList.remove('x');
	// 	bord.classList.add('o');
	// } else {
	// 	target.classList.add('o');
	// 	bord.classList.remove('o');
	// 	bord.classList.add('x');
	// }
	// 优化1-变量存储角色，实现代码2
	// target.classList.add(action);
	// bord.classList.remove(action);
	// // 切换角色 思路：根据当前玩家，切换到另一个玩家的
	// action = action === 'x' ? 'o' : 'x';
	// bord.classList.add(action);
	// 优化2-枚举变量存储角色
	target.classList.add(userPlayer);

	// 下了一次棋，steps加1
	steps++;
	// 测试代码
	console.log(steps);

	// 判赢函数的调用
	let isWin = checkWin(userPlayer);
	// 先判断输赢，再判断平局
	if (isWin) {
		// console.log(userPlayer + '获胜了');
		// 获胜了'游戏获胜信息面板'展示，同时面板上显示“哪一方获胜的消息”
		message.style.display = 'block';
		p.innerText = userPlayer + '获胜了！';
		return;
	}
	/* 
		 “重新开始” 功能中，cell.addEventListener('click',handleClick,{once:true}) {once:true}不能少。
		否则点一个X,再点一次X就变成了O,并一直保持O,直到游戏结束。原因是：cell.addEventListener('click',handleClick)
		覆盖了上面的cell点击函数，原因是cell="x o",{once:true}使得添加x类后，再次点击该区域无效(点击事件被移除了)，就不会
		出现同一个cell中有x和o两个类样式的情况。有两个同样功能的类样式，根据css的三大特性(层叠、继承、优先级)，后面层叠前面的
	*/
	if (steps === 9) {
		// console.log('这是个平局');
		message.style.display = 'block';
		p.innerText = '平局！';
		return;
	}
	// 特别注意：必须放在判赢函数后面，因为判赢函数中需要传入当前的userPlayer的值
	bord.classList.remove(userPlayer);
	// 切换角色 思路：判断当前玩家是x吗？是x就切换到o。是o就切换到x
	// 易错点：userPlayer赋值只可能是Play.A或者Play.B。不能代换写成'x'或者'o',即使打印结果是'x'或'o'
	userPlayer = userPlayer === Player.X ? Player.O : Player.X;
	// 此处的userPlayer值已经改变，为下一次下棋添加不同的提示做好准备
	bord.classList.add(userPlayer);
}

// 遍历cells列表
// cells.forEach(item => {
// 	let ele = item as HTMLDivElement;
// 	ele.addEventListener(
// 		'click',
// 		// function (event) {
// 		// 	console.log(event);
// 		// 	//  event.target本质上是一个cell,且有类型注解
// 		// 	let target = event.target as HTMLDivElement;
// 		// 	target.classList.add('x');
// 		// },
// 		handleClick,
// 		// 已经放置内容的x格子内，再次点击不应该再放x内容了，可以使用{once:true}来限制事件触发多次
// 		{ once: true }
// 	);
// });
/**
 * x放置完成了开始交替放置O了
 *技巧：在vscode界面鼠标指针放在'菜单栏'中，然后一直向左侧拖动，可以将
 *vscod界面和浏览器界面放置在同一个屏幕中
 *
 */
/**
 * 封装判赢函数
 *
 */
// 判赢函数封装
function checkWin(player: Player) {
	// some方法，返回值为布尔型。满足条件一次(9种获胜途径满足一种)，就返回true,遍历终止
	return winArr.some(item => {
		// 1.先拿到每种获胜情况的三个索引,然后根据索引拿到对应的对象，使用对象的api,来判定是否含当前类名
		let index1 = item[0];
		let index2 = item[1];
		let index3 = item[2];
		// 2.拿到某种获胜其情况下的三个cell对象
		// let cell1 = cells[index1];
		// let cell2 = cells[index2];
		// let cell3 = cells[index3];
		// 3.判断三个对象中是否同时包含当前类样式(Player.X或者Player.O)
		/**
		 * 重复的代码
		 * 封装一个hasClass函数 hasClass(cell,player: Player)
		 */
		if (hasClass(cells[index1], player) && hasClass(cells[index2], player) && hasClass(cells[index3], player)) {
			// 4.当某一时刻，获胜索引所在cell同时包含了一样的类样式，当前角色就获胜了
			return true; // 然后停止循环了
		}
		return false;
	});
}
/**
 * “重新开始”按钮点击事件-重置数据
 * a.隐藏获胜数据
 * b.清空棋盘
 * c.移除单元格点击事件（主要是针对游戏结束，还没被点击的方格，这里针对所有方格，结果是一样的）
 * d.重置下棋次数
 * e.bord重置为x
 * f.当前角色重置为x
 *
 */
btn.addEventListener('click', startGame);
function startGame() {
	// 1. 隐藏判赢信息面板
	message.style.display = 'none';
	// 4.重置下棋次数
	steps = 0;
	// 5.bord重置为x
	bord.classList.remove(userPlayer);
	// 6.当前角色为x
	userPlayer = Player.X;
	bord.classList.add(userPlayer);
	// 2.移除cells已经添加的'x'或者'o'类
	cells.forEach(item => {
		let cell = item as HTMLDivElement;
		// 2.清空棋盘
		cell.classList.remove(Player.X, Player.O);
		// 3.移除点击事件
		cell.removeEventListener('click', handleClick);
		// 添加点击事件
		cell.addEventListener('click', handleClick, { once: true });
	});
}
// 初始化调用一次
startGame();
/**
 * hasClass函数的封装
 *
 */
function hasClass(cell: Element, player: Player) {
	return cell.classList.contains(player);
}
